import React from 'react';
import _ from 'lodash';
import { Checkbox } from 'antd';
import styled from 'styled-components';
import BaseTable, { normalizeColumns } from './src/index';
import styles from './index.less';

const StyledTable = styled(BaseTable)`
  .row-selected {
    background-color: #e3e3e3;
  }
`;

class SelectionCell extends React.PureComponent {
  handleChange = e => {
    const {
      rowData,
      rowIndex,
      column: { onChange },
    } = this.props;
    onChange({ selected: e.target.checked, rowData, rowIndex });
  };

  render() {
    const { rowData, column } = this.props;
    const { selectedRowKeys, rowKey } = column;
    const checked = selectedRowKeys.includes(rowData[rowKey]);
    return <Checkbox checked={checked} onChange={this.handleChange} />;
  }
}

class SelectableTable extends React.PureComponent {
  constructor(props) {
    super(props);
    const {
      selectedRowKeys,
      defaultSelectedRowKeys,
      expandedRowKeys,
      defaultExpandedRowKeys,
    } = props;
    this.state = {
      selectedRowKeys:
        (selectedRowKeys !== undefined ? selectedRowKeys : defaultSelectedRowKeys) || [],
      expandedRowKeys:
        (expandedRowKeys !== undefined ? expandedRowKeys : defaultExpandedRowKeys) || [],
    };
  }

  /**
   * Set `selectedRowKeys` manually.
   * This method is available only if `selectedRowKeys` is uncontrolled.
   *
   * @param {array} selectedRowKeys
   */
  setSelectedRowKeys(selectedRowKeys) {
    // if `selectedRowKeys` is controlled
    if (this.props.selectedRowKeys !== undefined) return;

    this.setState({
      selectedRowKeys: _.cloneDeep(selectedRowKeys),
    });
  }

  /**
   * See BaseTable#setExpandedRowKeys
   */
  setExpandedRowKeys(expandedRowKeys) {
    // if `expandedRowKeys` is controlled
    if (this.props.expandedRowKeys !== undefined) return;

    this.setState({
      expandedRowKeys: _.cloneDeep(expandedRowKeys),
    });
  }

  /* some other custom methods and proxy methods */

  /**
   * Remove rowKeys from inner state  manually, it's useful to purge dirty state after rows removed.
   * This method is available only if `selectedRowKeys` or `expandedRowKeys` is uncontrolled.
   *
   * @param {array} rowKeys
   */
  removeRowKeysFromState(rowKeys) {
    if (!Array.isArray(rowKeys)) return;

    const state = {};
    if (this.props.selectedRowKeys === undefined && this.state.selectedRowKeys.length > 0) {
      state.selectedRowKeys = this.state.selectedRowKeys.filter(key => !rowKeys.includes(key));
    }
    if (this.props.expandedRowKeys === undefined && this.state.expandedRowKeys.length > 0) {
      state.expandedRowKeys = this.state.expandedRowKeys.filter(key => !rowKeys.includes(key));
    }
    if (state.selectedRowKeys || state.expandedRowKeys) {
      this.setState(state);
    }
  }

  handleSelectChange = ({ selected, rowData, rowIndex }) => {
    const selectedRowKeys = [...this.state.selectedRowKeys];
    const key = rowData[this.props.rowKey];

    if (selected) {
      if (!selectedRowKeys.includes(key)) selectedRowKeys.push(key);
    } else {
      const index = selectedRowKeys.indexOf(key);
      if (index > -1) {
        selectedRowKeys.splice(index, 1);
      }
    }

    // if `selectedRowKeys` is uncontrolled, update internal state
    if (this.props.selectedRowKeys === undefined) {
      this.setState({ selectedRowKeys });
    }
    this.props.onRowSelect([{ selected, rowData, rowIndex }]);
    this.props.onSelectedRowsChange(selectedRowKeys);
  };

  rowClassName = ({ rowData, rowIndex }) => {
    const { rowClassName, rowKey } = this.props;
    const { selectedRowKeys } = this.state;

    const rowClass = rowClassName ? callOrReturn(rowClassName, { rowData, rowIndex }) : '';
    const key = rowData[rowKey];

    return [rowClass, selectedRowKeys.includes(key) && 'row-selected'].filter(Boolean).concat(' ');
  };

  // 全选
  onChangeAll = e => {
    const { checked } = e.target;
    let selectedRowKeys = [];
    let all = [];
    if (checked) {
      selectedRowKeys = this.props.data.map(item => item.id);
      all = this.props.data.map((item, idx) => {
        return {
          selected: checked,
          rowData: item,
          rowIndex: idx,
        };
      });
    }
    this.setState({ selectedRowKeys });
    this.props.onRowSelect(all);
    this.props.onSelectedRowsChange(selectedRowKeys);
  };

  render() {
    const { columns, children, selectable, selectionColumnProps, ...rest } = this.props;
    const { selectedRowKeys } = this.state;
    const indeterminate = selectedRowKeys.length !== 0 && selectedRowKeys.length < rest.data.length;
    // you'd better memoize this operation
    let newColumns = columns || normalizeColumns(children);
    if (selectable) {
      const selectionColumn = {
        width: 45,
        flexShrink: 0,
        resizable: false,
        frozen: 'left',
        cellRenderer: SelectionCell,
        ...selectionColumnProps,
        key: '__selection__',
        rowKey: this.props.rowKey,
        selectedRowKeys,
        onChange: this.handleSelectChange,
        title: (
          <Checkbox
            checked={selectedRowKeys.length === rest.data.length}
            style={{ marginRight: 5 }}
            onChange={this.onChangeAll}
            indeterminate={indeterminate}
          />
        ),
      };
      newColumns = [selectionColumn, ...newColumns];
    }

    return (
      <StyledTable
        className={styles.baseTableStyle}
        {...rest}
        columns={newColumns}
        rowClassName={this.rowClassName}
      />
    );
  }
}

SelectableTable.defaultProps = {
  ...BaseTable.defaultProps,
  onRowSelect: () => {},
  onSelectedRowsChange: () => {},
};

export default SelectableTable;
